/*
 * GeometryCollection
 * 
 * initial implementation : 31.8.2007
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Petra.Gis.Geometry.Support;


namespace Petra.Gis.Geometry
{


  /*public abstract class GeometryCollection<T> : geometryBase, IList<T> where T : geometryBase
  public abstract class GeometryCollection<T1, T2> : geometryBase, IList<geometryBase>
    where T1 : geometryBase
    where T2 : geometryBase
   */

  /// <summary>
  /// GeometryCollection geometry is Collection of any geometry, except of Collections.
  /// It is not allowed to insert collection into collection
  /// input geometry types is threfore limited by typeLimits to Point, Label, Arc, LineString, Path, Polygon
  /// in this and to other types in derived classes
  /// </summary>
  public class GeometryCollection : GeometryBase, IList<GeometryBase>
  {
    /***********************************************
     * private holder of all geometries inside any
     * component geometry
     * 
     * 1.9.2007
     * asida@centrum.cz, initial implementation
    ************************************************/
    protected class component
    {
      //public T geometry;
      public GeometryBase geometry;
      public component next;
    }

    protected component firstItem;
    protected int ItemCount;
    protected System.Type[] typeLimits;



    public GeometryCollection()
    {
      System.Type[] _tl = {
                           typeof(Point),
                           typeof(Arc),
                           typeof(LineString),
                           typeof(Path),
                           typeof(Polygon),
                           //typeof(LineRing),
                           //typeof(PathRing),
                           typeof(Label)
                          };
      typeLimits = _tl;
      Clear();
    }



    //IEnumerator
    //public IEnumerator<T> GetEnumerator()
    public IEnumerator<GeometryBase> GetEnumerator()
    {
      component current = firstItem;
      while (current != null)
      {
        yield return current.geometry;
        current = current.next;
      }
    }
    



    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }
    
    //ICollection
    public void Clear()
    {
      firstItem = null;
      ItemCount = 0;
    }



    //public int IndexOf(T oGeom)
    public int IndexOf(GeometryBase oGeom)
    {
      int i = 1;
      component current = firstItem;
      while (current != null)
      {
        if (current.geometry.Equals(oGeom))
          return i;
        current = current.next;
        i++;
      }
      return -1;
    }




    protected bool validateType(System.Type CurrType)
    {
      foreach (System.Type i in typeLimits)
      {
        if (CurrType.Name == i.Name)
          return true;
      }
      return false;
    }




    public virtual void Add(GeometryBase oGeom)
    {
      if (!validateType(oGeom.GetType()))
        throw new ArgumentException("cannot Add "+ oGeom.GetType().ToString()+" into " + this.GetType().ToString());
      if (ItemCount == 0)
      {
        firstItem = new component();
        firstItem.geometry = oGeom;
        firstItem.next = null;
      }
      else
      {
        component current = firstItem;
        while (current.next != null)
        {
          current = current.next;
        }
        current.next = new component();
        current.next.geometry = oGeom;
        current.next.next = null;
      }

      ItemCount++;
    }




    public void Insert(int Index, GeometryBase oGeom)
    {
      //todo: Insert missing
    }




    //public bool Remove(T oGeom)
    public bool Remove(GeometryBase oGeom)
    {
      //todo: remove missing
      return false;
    }




    public void RemoveAt(int Index)
    {
      //todo: RemoveAt missing
    }




    //public bool Contains(T oGeom)
    public bool Contains(GeometryBase oGeom)
    {
      component current = firstItem;
      while (current.next != null)
      {
        if (current.Equals(oGeom))
          return true;
        current = current.next;
      }
      return false;
    }




    //public void CopyTo(T[] oGeom, int Index)
    public void CopyTo(GeometryBase[] oGeom, int Index)
    { 
    }




    public int Count
    {
      get { return ItemCount; }
    }




    public bool IsReadOnly
    {
      get { return false; }
    }




    //public T this[int Index]
    public GeometryBase this[int Index]
    {
      get
      {
        int i = 0;
        component current = firstItem;
        while(i++!=Index)
        {
          current = current.next;
        }
        return current.geometry;
      }
      set
      {
        //todo: set part is missing
      }
    }




    internal override BoundingBox getEnvelope()
    {
      double xlo, ylo;
      double xhi, yhi;
      int i;
      BoundingBox tmp;

      //get first geometry envelope
      tmp = this[0].getEnvelope();

      xlo = tmp.Left;
      ylo = tmp.Bottom;
      xhi = tmp.Right;
      yhi = tmp.Top;

      for(i = 1; i < this.Count; i++)
      {
        tmp = this[i].getEnvelope();

        if (xlo > tmp.Left)
          xlo = tmp.Left;

        if (ylo > tmp.Bottom)
          ylo = tmp.Bottom;

        if (xhi < tmp.Right)
          xhi = tmp.Right;

        if (yhi < tmp.Top)
          yhi = tmp.Top;
      }

      _envelope = new BoundingBox(xlo, ylo, xhi, yhi);

      return _envelope;
    }


  }
}
